package com.ldodds.deville.rdf;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.net.URL;
import java.text.MessageFormat;

import com.hp.hpl.jena.query.DataSource;
import com.hp.hpl.jena.query.DatasetFactory;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.Syntax;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.ldodds.deville.calais.CalaisException;
import com.ldodds.deville.calais.OpenCalais;
import com.ldodds.deville.calais.Parameters;
import com.ldodds.deville.calais.ParametersImpl;
import com.ldodds.deville.util.XML;

public class DevilleImpl implements Deville
{
	private OpenCalais client;
	
	public DevilleImpl() {		
	}

	public DevilleImpl(OpenCalais client) {
		this.client = client;
	}
	
	public Model enlighten(String content) 
		throws IOException, CalaisException {
		Parameters parameters = new ParametersImpl();
		parameters.setOutputFormat(Parameters.OUTPUT_RDF);
		return this.enlighten(content, parameters );
	}

	public Model enlighten(String content, Parameters parameters) 
		throws IOException, CalaisException {
		
		String response = client.enlighten(content, parameters);
		Model model = ModelFactory.createDefaultModel();
		model.read( new StringBufferInputStream(response), "");
		return model;
	}

	public Model enlighten(InputStream stream)
		throws IOException, CalaisException {
		Parameters parameters = new ParametersImpl();
		parameters.setOutputFormat(Parameters.OUTPUT_RDF);	
		return this.enlighten(stream, parameters );
	}

	public Model enlighten(InputStream stream, Parameters parameters)
		throws IOException, CalaisException {		
		String response = client.enlighten(stream, parameters);
		Model model = ModelFactory.createDefaultModel();
		model.read( new StringBufferInputStream(response), "");
		return model;
	}

	public Model enlighten(File file, Parameters parameters)
	throws IOException, CalaisException {
		return enlighten( new FileInputStream(file), parameters );
	}

	public Model enlighten(URL url, Parameters parameters)
	throws IOException, CalaisException {
		return enlighten( url.openStream() , parameters );
	}
	
	public OpenCalais getClient() {
		return client;
	}

	public void setClient(OpenCalais client) {
		this.client = client;
	}

	public Model enlighten(Model data, String sparql)
		throws IOException, CalaisException {
		Parameters parameters = new ParametersImpl();
		Query query = QueryFactory.create( sparql, Syntax.syntaxSPARQL );		
		return enlighten(data, query, parameters);
	}

	public Model enlighten(Model data, Query sparql)
	throws IOException, CalaisException {
		Parameters parameters = new ParametersImpl();
		return enlighten(data, sparql, parameters);
	}
	
	public Model enlighten(Model data, String sparql, Parameters parameters)
			throws IOException, CalaisException {
		Query query = QueryFactory.create( sparql, Syntax.syntaxSPARQL );		
		return enlighten(data, query, parameters);
	}

	public Model enlighten(Model data, Query sparql, Parameters parameters)
		throws IOException, CalaisException {

		parameters.setOutputFormat(Parameters.OUTPUT_RDF);
		parameters.setContentType(Parameters.TEXT_XML);		
		
		DataSource dataSet = DatasetFactory.create(data);
		QueryExecution execution = QueryExecutionFactory.create(sparql, dataSet);
		ResultSet results = execution.execSelect();
		
		Model model = ModelFactory.createDefaultModel();
		
		while ( results.hasNext() ) {
			QuerySolution solution = results.nextSolution();
			//FIXME check for OPTIONAL, missing values, etc
			String title = getString(solution, Deville.TITLE);
			String description = getString(solution, Deville.DESCRIPTION);
			//TODO OpenCalais doesn't document its date format?
			String date = getString(solution, Deville.DATE);
			String inputDocument = getInputDocument( title, description, date );
			Model result = enlighten(inputDocument, parameters);
			model.add( result );
		}
		
		return model;
	}
	
	private String getString(QuerySolution solution, String varName) {
		return solution.getLiteral( varName ).getLexicalForm();		
	}
	
	protected String getInputDocument(String title, 
				String description, String date) {
		final String template = "<DOCUMENT>" +
				"<TITLE>{0}</TITLE>" +
				"<DATE>{1}</DATE>" +
				"<DESCRIPTION>{2}</DESCRIPTION>" +
				"</DOCUMENT>";
		Object[] arguments = new Object[3];
		arguments[0] = XML.escapeXML( title );
		arguments[1] = XML.escapeXML( date );
		arguments[2] = XML.escapeXML( description );
		
		return MessageFormat.format(template, arguments);		
 	}
}
